/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "ScanMethod6Ping.h"

//---------------------------------------------------------------------------

ScanMethod6Ping::ScanMethod6Ping(void)
{
	myType = __wmt6Ping;
}

ScanMethod6Ping::~ScanMethod6Ping(void)
{
}

//---------------------------------------------------------------------------
int ScanMethod6Ping::InternalInit(void)
{
	FillRandomBuffer();
	
	if(workPhase==__wptDiscovery) return MSR_ERR_OK;
	if(workPhase==__wptScan)      return MSR_ERR_OK;

	return MSR_ERR_PHASE;
}

int ScanMethod6Ping::InternalExecute(void)
{
	ScanPacket* pack;

	if(addrList==NULL) return MSR_ERR_PARAMETER;

	ScanPair sp;
	while(GetNextPair(&sp, __sptIpOnly) && !stopStopStop){
		if(PacketsUsed()>0){
			CheckResponses();
			CheckDeaths();
		}

		if(sp.addr!=NULL){
			if(!sp.addr->addrIPv6.IsEmpty()){
				if(workPhase==__wptDiscovery && sp.addr->alive){
					// it's the discovery phase and a previous discovery method has found it already
					// so skip this one
				}
				else{
					if(config->xMinDelay>0){
						if(lastScanTime!=0){
							waitDone = false;
							while(!waitDone){
								CheckResponses();
								SleepEx(1, TRUE);
								scanTime = GetTickCount();
								scanTime = ComputeDeltaTime(lastScanTime, scanTime);
								if(scanTime>=config->xMinDelay)
									waitDone = true;
							}
						}
						lastScanTime = GetTickCount();
					}

					pack = FindFirstUnusedPacket();
					while(pack==NULL){
						SleepEx(waitTime, TRUE);
						CheckResponses();
						CheckDeaths();
						pack = FindFirstUnusedPacket();
					}

					if(pack!=NULL){
						pack->Initialize(true);
						pack->sTime       = GetTickCount();
						sp.addr->addrIPv6.Get(&pack->addr6);
						pack->port        = 0;
						pack->methodType  = myType;
						pack->fullAddress = sp.addr;
						pack->machineInfo = FindMachineByIp6(&(pack->addr6));

						if(!SendIPv6Ping(pack, sp.addr))
							pack->Initialize(false);

						if((packSent % notifTcp) == 0 && packSent>0)
							if(hWnd!=NULL)
								PostMessage(hWnd, CHS_MSG_PACKETS_SENT, 0, (LPARAM)packSent);
					}
				}
			}
		}
	}

	// wait until the end
	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

bool ScanMethod6Ping::SendIPv6Ping(ScanPacket* packIn, MFullAddress* fullAddrIn)
{
	BYTE     srcMAC[6];
	in_addr6 srcIPv6;
	BYTE     *payload;
	int      payloadLength;
	bool     res;

	if(packIn==NULL)                  return false;
	if(fullAddrIn==NULL)              return false;
	if(fullAddrIn->addrMAC.IsEmpty()) return false;

	winPcap->FillSrcMAC(&srcMAC[0]);
	winPcap->GetIPv6UnicastAddress(&srcIPv6);

	if(scanParameters.GetLength()==0){
		res = packetBuilderEx6->BuildPackIcmpEchoRequest(&srcIPv6, &(packIn->addr6));
	}
	else{
		payload = (BYTE*)(const char*)scanParameters;
		payloadLength = scanParameters.GetLength();
		if(payloadLength>1024)
			payloadLength = 1024;
		res = packetBuilderEx6->BuildPackIcmpEchoRequestWithUserData(&srcIPv6, &(packIn->addr6), payload, payloadLength);
	}
	if(!res) return false;

	if(!packetBuilderEx6->AddEthernetHeader(srcMAC, fullAddrIn->addrMAC.addr))
		return false;

	return SendPacket(packetBuilderEx6->GetPacket(), (int)packetBuilderEx6->GetPacketSize());
}

//--------------------------------------------------------------------

void ScanMethod6Ping::ProcessEchoReply6(BYTE* dataIn, int sizeIn, BYTE* srcIP, BYTE* srcMAC)
{
	ScanPacket   *pack;
	MMachineInfo *mach;
	MFullAddress *addr;
	in_addr6 ia;

	if(sizeIn<sizeof(ICMP6EchoPG)) return;

	memcpy(ia.u.Byte, srcIP, 16);

	pack = FindPacket6(&ia, 0, myType);
	if(pack==NULL) return;

	if(workPhase==__wptDiscovery){
		addr = pack->fullAddress;
		if(addr!=NULL)
			addr->alive = true;
	}
	else{
		mach = pack->machineInfo;
		if(mach!=NULL)
			mach->pingReplayTime = ComputeDeltaTime(pack->sTime, GetTickCount());
	}

	pack->Initialize(false);
}

//---------------------------------------------------------------------------

void ScanMethod6Ping::ProcessDestinationUnreachable6(BYTE* dataIn, int sizeIn, BYTE* srcIP)
{
	ScanPacket  *pack;
	IP6HeaderPG *iph;
	in_addr6    ia;

	if(sizeIn<sizeof(IP6HeaderPG)) return;

	iph = (IP6HeaderPG*)dataIn;

	memcpy(ia.u.Byte, iph->Destination, 16);

	pack = FindPacket6(&ia, 0, myType);
	if(pack!=NULL)
		pack->Initialize(false);
}

//---------------------------------------------------------------------------
